package cn.uncode.dal.criteria;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import cn.uncode.dal.annotation.Table;
import cn.uncode.dal.core.BaseDTO;
import cn.uncode.dal.descriptor.ForeignKey;
import cn.uncode.dal.utils.BeanUtil;
import cn.uncode.dal.utils.IgnoreCaseHashMap;

public class Model implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 3485804608796833321L;

    private IgnoreCaseHashMap<String, Object> content;
    
    private List<Map<String, Object>> contentList;

    private Map<String, Object> context;
    
    private Class<?> clazz;

    public static final String MODEL_NAME = "table_name";

    public static final String DATA_BASE = "data_base";

    public static final String MODEL_ID = "single_primary_key";
    
    public static final String VERSION = "version";

    public Model(String modelName) {
        content = new IgnoreCaseHashMap<String, Object>();
        context = new HashMap<String, Object>();
        contentList = new ArrayList<Map<String, Object>>();
        context.put(MODEL_NAME, modelName.toLowerCase().trim());
    }

    public Model(String database, String modelName) {
        content = new IgnoreCaseHashMap<String, Object>();
        context = new HashMap<String, Object>();
        contentList = new ArrayList<Map<String, Object>>();
        context.put(MODEL_NAME, modelName.toLowerCase().trim());
        if(StringUtils.isNotBlank(database)) {
        	context.put(DATA_BASE, database.toLowerCase().trim());
        }
    }

    public Model(Class<?> clazz) {
        content = new IgnoreCaseHashMap<String, Object>();
        context = new HashMap<String, Object>();
        contentList = new ArrayList<Map<String, Object>>();
        this.clazz = clazz;
        Table table = clazz.getAnnotation(Table.class);
        String name = "";
        if(table != null){
        	name = table.name();
        }else{
        	name = clazz.getName();
        	name = name.substring(name.lastIndexOf(".") + 1);
        }
        context.put(MODEL_NAME, name.toLowerCase().trim());
    }
    
    public Model(Object obj) {
    	this(null, obj);
    }

    public Model(String database, Object obj) {
		content = new IgnoreCaseHashMap<String, Object>();
        context = new HashMap<String, Object>();
        contentList = new ArrayList<Map<String, Object>>();
        Map<String, Object> map = null;
    	if(obj instanceof Model){
    		Model temp = (Model) obj;
    		content = temp.getContent();
    		context = temp.getContext(); 
    		contentList = temp.getContentList();
    	}else if(obj instanceof List){
    		//不实现
    	}else if(obj instanceof Map){
    		map = (Map<String, Object>) obj;
        	content.putAll(map);
    	}else{
        	Table table = obj.getClass().getAnnotation(Table.class);
        	this.clazz = obj.getClass();
            String name;
            if(table != null){
            	name = table.name();
            }else{
            	if(obj instanceof String){
            		name = String.valueOf(obj);
            	}else if(obj instanceof Class){
            		name =  ((Class)obj).getName();
            	}else{
            		name =  obj.getClass().getName();
            	}
            	name = name.substring(name.lastIndexOf(".") + 1);
            }
            map = BeanUtil.objToMap(obj);
    		content.putAll(map);
            context.put(MODEL_NAME, name.toLowerCase().trim());
            
            if(StringUtils.isNotEmpty(database)){
            	context.put(DATA_BASE, database.toLowerCase().trim());
            }
            if(null != map && map.containsKey(BaseDTO.ID)){
            	this.setSinglePrimaryKey(map.get(BaseDTO.ID));
            }
    	}
    }

    public Model(String modelName, Map<String, Object> obj) {
        context = new HashMap<String, Object>();
        content = new IgnoreCaseHashMap<String, Object>();
        contentList = new ArrayList<Map<String, Object>>();
        context.put(MODEL_NAME, modelName.toLowerCase().trim());
        content.putAll(obj);
        if(null != obj && obj.containsKey(BaseDTO.ID)){
        	this.setSinglePrimaryKey(obj.get(BaseDTO.ID));
        }
    }
    
    public Model newForeignKeyModel(ForeignKey foreignKey) {
    	Model mod = new Model(getDatabase(), foreignKey.getTbName());
		mod.setVersion(getVersion());
		mod.setClazz(foreignKey.getFdClass());
		if(null != foreignKey.getParams()) {
			mod.addContent(foreignKey.getParams());
		}
		if(foreignKey.hasForeignKey()) {
			for(ForeignKey fk : foreignKey.getForeignKeys()) {
				if(BeanUtil.idIsNotNull(fk.getId())) {
					mod.getContent().remove(fk.getFdName());
					mod.getContent().put(fk.getFdTableName(), fk.getId());
				}
			}
		}
		return mod;
    }
    
    public void processForeignKey(Set<ForeignKey> foreignKeys) {
    	if(null != foreignKeys) {
    		for(ForeignKey foreignKey : foreignKeys) {
    			if(BeanUtil.idIsNotNull(foreignKey.getId())) {
    				content.remove(foreignKey.getFdName());
    				content.put(foreignKey.getFdTableName(), foreignKey.getId());
				}
    		}
    	}
    }

    public void setSinglePrimaryKey(Object primaryKey) {
        context.put(MODEL_ID, primaryKey);
    }

    public void setDatabase(String database) {
        context.put(DATA_BASE, database);
    }
    
    public void setTableName(String tableName) {
        context.put(MODEL_NAME, tableName);
    }
    
    public void setVersion(Object version) {
        context.put(VERSION, version);
    }

    public Object getSinglePrimaryKey() {
    	Object id = context.get(MODEL_ID);
    	if(null == id){
    		id = content.get(BaseDTO.ID);
    	}
        return id;
    }
    
    public boolean hasPrimaryKey() {
    	Object id = getSinglePrimaryKey();
    	if(id == null) {
    		return false;
    	}
    	String idStr = String.valueOf(id).trim();
    	if(StringUtils.isBlank(idStr)) {
    		return false;
    	}
    	if("0".equals(idStr)) {
    		return false;
    	}
    	return true;
    }

    public String getDatabase() {
        return (String) context.get(DATA_BASE);
    }

    public String getTableName() {
        return (String) context.get(MODEL_NAME);
    }
    
    public Object getVersion() {
        return content.get(VERSION);
    }

    public void setField(String field, Object value) {
        if (StringUtils.isEmpty(field)) {
            return;
        }
        content.put(field, value);
    }

    public Object getField(String field) {
        if (StringUtils.isEmpty(field)) {
            return null;
        }
        return content.get(field);
    }

    public IgnoreCaseHashMap<String, Object> getContent() {
        return content;
    }

    public void addContent(Map<String, Object> content) {
        this.content.putAll(content);
    }
    
    public Map<String, Object> getContext() {
		return context;
	}

	public List<Map<String, Object>> getContentList() {
		return contentList;
	}

	public void setClazz(Class<?> clazz) {
		this.clazz = clazz;
	}

	public Class<?> getClazz() {
		return clazz;
	}

	@Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + content.hashCode();
        result = prime * result + context.hashCode();
        return result;
    }
    
    private String firstToLower(String str) {
        char[] array = str.toCharArray();
        array[0] += 32;
        return String.valueOf(array);
    }

}
